package com.zcy.gift.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.zcy.gift.client.GoodSkuClient;
import com.zcy.gift.domain.GoodsSku;
import com.zcy.gift.domain.ShopCar;
import com.zcy.gift.dto.AddShopCarDTO;
import com.zcy.gift.dto.DeleteDTO;
import com.zcy.gift.dto.UpdateNumDTO;
import com.zcy.gift.dto.UpdateSelectDTO;
import com.zcy.gift.exception.BusinessException;
import com.zcy.gift.exception.ResponseCode;
import com.zcy.gift.exception.RonghuaAssert;
import com.zcy.gift.mapper.ShopCarMapper;
import com.zcy.gift.service.IShopCarService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.zcy.gift.strategy.ShopCarDataSyncKeyGroupStrategyFactory;
import com.zcy.gift.util.AjaxResult;
import com.zcy.gift.util.TenantIdName;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zcy
 * @since 2024-02-20
 */
@Service
public class ShopCarServiceImpl extends ServiceImpl<ShopCarMapper, ShopCar> implements IShopCarService {

    @Autowired
    private GoodSkuClient goodSkuClient;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private static final String PREFIX = "shop_car_prefix_";

    @Value("${gift.shopcar.strategy.group.key}")
    private String keyGroupStrategy;

    @Override
    @Transactional
    public void addShopCar(AddShopCarDTO dto) {
        // 查询redis中数据
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(PREFIX + dto.getUserId());

        List<String> values = hashOps.values();

        // 检测是否需要从数据库中获取
        if (values == null || values.isEmpty()) {
            // redis中不存在时，查询数据库，将数据库中的数据存入redis
            Wrapper<ShopCar> shopCarEntityWrapper = new EntityWrapper<ShopCar>().eq("user_id", dto.getUserId());
            List<ShopCar> shopCars = selectList(shopCarEntityWrapper);
            for (ShopCar shopCar : shopCars) {
                hashOps.put(shopCar.getSkuId().toString(), JSONObject.toJSONString(shopCar));
            }
        }
        // 再次判断是否redis中数据是否为空
        List<String> shopCarJSONStr = hashOps.values();
        List<ShopCar> shopCars = new ArrayList<>();
        Boolean isShopCarExist = false;
        if (shopCarJSONStr != null && !shopCarJSONStr.isEmpty()) {
            // 不为空则判断该sku信息是否存在
            // 从redis中获取实例
            for (String jsonStr : shopCarJSONStr) {
                ShopCar shopCar = JSONObject.parseObject(jsonStr, ShopCar.class);
                if (Objects.equals(shopCar.getSkuId(), dto.getSkuId())) {
                    isShopCarExist = true;
                    // 存在则修改数量
                    // 判断是否已删除
                    if (shopCar.getIsDelete()) {
                        // 已删除则直接覆盖
                        shopCar = getShopCar(dto);
                    } else {
                        // 未被删除则只更新数量
                        shopCar.setNum(shopCar.getNum() + dto.getNum());
                    }
                }
                shopCars.add(shopCar);
            }
            // 不存在则直接新增
            if (!isShopCarExist) {
                shopCars.add(getShopCar(dto));
            }
        } else {
            // 为空直接新增
            shopCars.add(getShopCar(dto));
        }
        // 将数据存入redis
        for (ShopCar shopCar : shopCars) {
            hashOps.put(shopCar.getSkuId().toString(), JSONObject.toJSONString(shopCar));
        }
        hashOps.expire(26, TimeUnit.HOURS);
    }

    @Override
    public void updateNum(UpdateNumDTO dto) {
        // 查询redis中数据
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(PREFIX + dto.getUserId());

        List<String> values = hashOps.values();
        RonghuaAssert.isNotNull(values, ResponseCode.RESPONSE_CODE_40033);
        String jsonStr = hashOps.get(dto.getSkuId().toString());
        if (StringUtils.isBlank(jsonStr)) {
            throw new BusinessException(ResponseCode.RESPONSE_CODE_40033);
        }
        ShopCar shopCar = JSONObject.parseObject(jsonStr, ShopCar.class);
        shopCar.setNum(dto.getNum());

        // 存入redis
        hashOps.put(dto.getSkuId().toString(), JSONObject.toJSONString(shopCar));
        // 刷新过期时间
        hashOps.expire(26, TimeUnit.HOURS);

    }

    @Override
    public void updateSelect(UpdateSelectDTO dto) {
        // 查询redis中数据
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(PREFIX + dto.getUserId());

        List<String> values = hashOps.values();
        RonghuaAssert.isNotNull(values, ResponseCode.RESPONSE_CODE_40033);
        String jsonStr = hashOps.get(dto.getSkuId().toString());
        if (StringUtils.isBlank(jsonStr)) {
            throw new BusinessException(ResponseCode.RESPONSE_CODE_40033);
        }
        ShopCar shopCar = JSONObject.parseObject(jsonStr, ShopCar.class);
        shopCar.setIsSelect(dto.getSelect());

        // 存入redis
        hashOps.put(dto.getSkuId().toString(), JSONObject.toJSONString(shopCar));
        // 刷新过期时间
        hashOps.expire(26, TimeUnit.HOURS);
    }

    @Override
    public Map<TenantIdName, List<ShopCar>> getShopCarList(Long userId) {
        // 查询redis中数据
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(PREFIX + userId);

        List<String> values = hashOps.values();
        if (values == null || values.isEmpty()) {
            Wrapper<ShopCar> wrapper = new EntityWrapper<ShopCar>().eq("user_id", userId);
            List<ShopCar> shopCars = selectList(wrapper);
            // 放入redis
            for (ShopCar shopCar : shopCars) {
                hashOps.put(shopCar.getSkuId().toString(), JSONObject.toJSONString(shopCar));
            }
            List<ShopCar> collect = shopCars.stream().filter(x -> !x.getIsDelete()).collect(Collectors.toList());
            return collect.stream().collect(Collectors.groupingBy(x -> new TenantIdName(x.getTenantId(), x.getTenantName())));
        }
        List<ShopCar> collect = values.stream().map(x -> JSONObject.parseObject(x, ShopCar.class)).filter(x -> !x.getIsDelete()).collect(Collectors.toList());
        // 更新redis的过期时间
        Map<TenantIdName, List<ShopCar>> tenantIdNameListMap = collect.stream().collect(Collectors.groupingBy(x -> new TenantIdName(x.getTenantId(), x.getTenantName())));
        hashOps.expire(26, TimeUnit.HOURS);
        return tenantIdNameListMap;
    }

    @Override
    public void deleteByRedis(DeleteDTO dto) {
        // 查询redis中数据
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(PREFIX + dto.getUserId());

        List<String> values = hashOps.values();
        RonghuaAssert.isNotNull(values, ResponseCode.RESPONSE_CODE_40033);
        String jsonStr = hashOps.get(dto.getSkuId().toString());
        if (StringUtils.isBlank(jsonStr)) {
            throw new BusinessException(ResponseCode.RESPONSE_CODE_40033);
        }
        ShopCar shopCar = JSONObject.parseObject(jsonStr, ShopCar.class);
        shopCar.setIsDelete(true);

        // 存入redis
        hashOps.put(dto.getSkuId().toString(), JSONObject.toJSONString(shopCar));
        // 刷新过期时间
        hashOps.expire(26, TimeUnit.HOURS);
    }

    @Override
    @Transactional
    public void dataSync() {
        // 获取redis中的key
        Set<String> keys = redisTemplate.keys(PREFIX + "*");
        // 对keys进行分组
        // 策略模式 + 工厂模式
        Map<Integer, List<String>> collect = ShopCarDataSyncKeyGroupStrategyFactory.getStrategy(keyGroupStrategy).group(keys);
        Set<Map.Entry<Integer, List<String>>> entries = collect.entrySet();
        List<Long> deleteIdList = new ArrayList<>();
        List<ShopCar> updateList = new ArrayList<>();
        for (Map.Entry<Integer, List<String>> entry : entries) {
            List<String> groupKeys = entry.getValue();
            // 从Redis中获取分组key对应的购物车信息
            for (String key : groupKeys) {
                // 获取购物车信息
                BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(key);
                List<String> shopCarJsonStrList = hashOps.values();

                // 根据是否删除分组
                Map<Boolean, List<ShopCar>> shopCarGroupByIsDelete = shopCarJsonStrList.stream().map(x -> JSONObject.parseObject(x, ShopCar.class)).collect(Collectors.groupingBy(x -> x.getIsDelete()));
                deleteIdList.addAll(shopCarGroupByIsDelete.get(true).stream().map(ShopCar::getId).collect(Collectors.toList()));
                updateList.addAll(shopCarGroupByIsDelete.get(false));
            }

            // 分组同步
            if (!deleteIdList.isEmpty()) {
                deleteBatchIds(deleteIdList);
            }
            if (!updateList.isEmpty()) {
                insertOrUpdateBatch(updateList);
            }
            System.out.println(entry.getKey() + "组 -> 同步已完成");
        }

    }

    /**
     * 通过前端参数封装购物车对象
     *
     * @param dto
     * @return
     */
    private ShopCar getShopCar(AddShopCarDTO dto) {
        // 获取sku信息
        AjaxResult ajaxResult = goodSkuClient.get(dto.getSkuId());
        if (!ajaxResult.isSuccess()) {
            throw new BusinessException(ajaxResult.getMessage());
        }
        if (ajaxResult.getResultObj() == null) {
            throw new BusinessException(ResponseCode.RESPONSE_CODE_40033);
        }

        GoodsSku goodsSku = JSONObject.parseObject(JSONObject.toJSONString(ajaxResult.getResultObj()), GoodsSku.class);

        // 封装购物车信息
        ShopCar shopCar = new ShopCar()
                .setSpuId(goodsSku.getSpuId())
                .setSpuName(goodsSku.getSpuName())
                .setSkuId(goodsSku.getId())
                .setNum(dto.getNum())
                .setTenantId(goodsSku.getTenantId())
                .setUserId(dto.getUserId())
                .setUsername(dto.getUsername())
                .setIsSelect(true)
                .setAddPrice(goodsSku.getPrice())
                .setCreateTime(new Date());

        return shopCar;
    }


}
